package com.example.assignment7.aspect;

import com.sun.istack.internal.logging.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Aspect
@Component
public class StopWatchAspect {


    private static final String FORMAT_PATTERN_DAY = "yyyy-MM-dd";
    private static final String FORMAT_PATTERN_HOUR = "yyyy-MM-dd HH";
    private static final String FORMAT_PATTERN_MILLS = "yyyy-MM-dd HH:mm:ss:SSS";

    private static final Logger logger = Logger.getLogger(StopWatchAspect.class);
    /**
     * 次数统计结果表，key是方法的signature,value是总次数
     */
    final Map<String,Integer> useTime= Collections.synchronizedMap(new HashMap<>());

    /**
     * 发生异常统计表
     */
    final Map<String,Integer> exmetrics=Collections.synchronizedMap(new HashMap<>());

    /**
     *
     */
    final Map<String,List<Double>> metrics=Collections.synchronizedMap(new IdentityHashMap<>());



    private String dateFormat(String pattern) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        return dateFormat.format(new Date());
    }

    @Around("execution(* com.example.assignment7.controller.*.*(..))")
    protected Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String requestURI = request.getRequestURI();
        Signature signature = joinPoint.getSignature();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        try {
            Object result = joinPoint.proceed();
            stopWatch.stop();

            String name= signature.getName();
            double seconds=stopWatch.getTotalTimeSeconds();

            if(useTime.containsKey(name)){
                useTime.put(name,(useTime.get(name)+1));
            }else useTime.put(name,1);

            if(metrics.containsKey(name)){
                metrics.get(name).add(seconds);
            }else {
                List<Double> list=new ArrayList<>();
                metrics.put(name,list);
                metrics.get(name).add(seconds);
            }

            logger.info("接口:" +requestURI + "; 方法名："+name+ "; cost: " + seconds +
                    "秒;调用次数"+useTime.get(name)+"次;最长响应时间:"+getMaxValue(name, metrics)
                    +"秒;最短响应时间:"+getMinValue(name,metrics)+"秒;平均响应时间"+getAvgValue(name,metrics)+"秒");
            return result;
        } catch (Exception e) {
            String exceptionName = e.getClass().getName();
            Integer base = exmetrics.get(exceptionName);
            exmetrics.put(exceptionName,base==null?1:base+1);
            exmetrics.forEach((ex,count)->{
                logger.info(" "+ex+":"+count+"\n");
            });
            logger.logSevereException(e);
            throw e;
        }
    }

    public Map<String,Integer> getUseTime() {
        return useTime;
    }

    public Map<String,List<Double>> getMetrics(){
        return metrics;
    }

    public Map<String,Integer> getExmetrics(){
        return exmetrics;
    }

    /**
     * 求Map<K,V>中Value(值)的最小值
     *
     * @param map
     * @return
     */
    public static Object getMinValue(String name,Map<String, List<Double>> map) {
        if (map == null)
            return null;
        List<Double> list=map.get(name);
        return Collections.min(list);
    }

    /**
     * 求Map<K,V>中Value(值)的最大值
     */
    public static Object getMaxValue(String name,Map<String, List<Double>> map) {
        if (map == null)
            return null;
        List<Double> list=map.get(name);
        return Collections.max(list);
    }

    /**
     * 求Map<k,v>中value的平均值
     */
    public static Object getAvgValue(String name,Map<String, List<Double>> map) {
        if (map == null)
            return null;
        List<Double> list=map.get(name);
        return  list.stream().mapToDouble(Double::valueOf).average().getAsDouble();
    }
}
